package chai.ningning.foundation.support;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.Calendar;

/**
 * 本类是一个用来操作日期的工具类，为降低大家阅读源码的难度，本次没有使用 JDK 8 提供 java.time 包中的类
 */
public final class DateHelper {

    private static final long SECOND = 1000L ;
    private static final long MINUTE = SECOND * 60 ; // 不考虑闰秒
    private static final long HOUR = MINUTE * 60 ;
    private static final long DAY = HOUR * 24 ;
	
	private static final Calendar CALENDAR = Calendar.getInstance();

	private static final java.util.Date CURRENT = new java.util.Date();
	
	private static final String DATE_PATTERN = "yyyy-MM-dd" ;
	private static final DateFormat DATE_FORMAT = new SimpleDateFormat( DATE_PATTERN );
	
	private static final String TIME_PATTERN = "HH:mm:ss" ;
	private static final DateFormat TIME_FORMAT = new SimpleDateFormat( TIME_PATTERN );
	
	private static final String DATE_TIME_PATTERN = "yyyy-MM-dd  HH:mm:ss" ;
	private static final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat( DATE_TIME_PATTERN );
	
	static {
        CALENDAR.clear(); // 清理 CALENDAR 中各个 日历字段 的值
	}

    /**
     *  返回当前时间对应的 java.util.Date 对象
     * @return
     */
    public static final java.util.Date now(){
	    CURRENT.setTime( System.currentTimeMillis() );
        java.util.Date date = (java.util.Date)CURRENT.clone() ;
	    return  date ;
    }
	
	/**
	 * 返回指定的 年月日对应的 Date 对象 (时间是当前的0:0:0.0)
	 * @param year 指定的年份
	 * @param month 指定的月份，范围是 1 到 12 
	 * @param date 指定的日期，范围可以是 1 到 31 
	 * @return 返回一个 java.util.Date 类型的对象
	 */
	public static final java.util.Date getDate( int year , int month , int date ){
		CALENDAR.set( year , month - 1 , date );
		java.util.Date ud = CALENDAR.getTime(); 
		return ud ;
	}
	
	/**
	 * 返回指定的 年月日对应的 Date 对象 (时间是当前的0:0:0.0)
	 * @param year 指定的年份
	 * @param month 指定的月份，范围是 1 到 12 
	 * @param date 指定的日期，范围可以是 1 到 31 
	 * @return 返回一个 java.sql.Date 类型的对象
	 */
	public static final java.sql.Date getSQLDate( int year , int month , int date ){
		CALENDAR.set( year , month - 1 , date );
		long ms = CALENDAR.getTimeInMillis();
		java.sql.Date sd = new java.sql.Date( ms );
		return sd ;
	}
	
	/**
	 * 将 java.util.Date 重新构造成 java.sql.Date
	 * @param ud 需要处理的 java.util.Date 对象
	 * @return
	 */
	public static final java.sql.Date cast( java.util.Date ud ) {
		if( ud != null ){
			long time = ud.getTime() ;
			return new java.sql.Date( time ) ;
		}
		return null ;
	}
	
	/**
	 * 将指定的 日期对象 格式化 为 yyyy-MM-dd 形式
	 * @param date 需要格式化的日期
	 * @return 日期对应的字符串
	 */
	public static final String dateFormat( java.util.Date date ) {
		return DATE_FORMAT.format( date );
	}
	
	/**
	 * 将指定的 日期对象 格式化 为 HH:mm:ss 形式
	 * @param date 需要格式化的日期
	 * @return 时间对应的字符串
	 */
	public static final String timeFormat( java.util.Date date ) {
		return TIME_FORMAT.format( date );
	}
	
	/**
	 * 将指定的 日期对象 格式化为 yyyy-MM-dd  HH:mm:ss 格式
	 * @param date 需要格式化的日期
	 * @return 日期和时间对应的字符串
	 */
	public static final String datetime( java.util.Date date ) {
		return DATE_TIME_FORMAT.format( date );
	}
	
	/**
	 * 将日期字符串按照 "yyyy-MM-dd" 模式解析为 日期对象
	 * @param source 需要解析的表示日期的字符串
	 * @return 解析成功返回 相应的日期对象，否则返回 null
	 */
	public static final java.util.Date parseDate( String source ) {
		java.util.Date date = null ;
		try {
			date = DATE_FORMAT.parse(source);
		} catch ( ParseException e ) {
			System.out.println( "解析 " + source + " 为 日期时发生错误，它可能不符合 " +  DATE_PATTERN + " 模式");
		}
		return date ;
	}

	/**
	 * 当第一个参数first所表示的时间比第二个参数second所表示的时间<b>早</b>时，返回true
	 * @param first 待比较的第一个Date对象
	 * @param second 待比较的第二个Date对象
	 * @return
	 */
	public static final boolean before( java.util.Date first , java.util.Date second ){
		if( first !=null && second != null ){
			return first.before( second );
		}
		return false ;
	}

	/**
	 * 当第一个参数first所表示的时间跟第二个参数second所表示的时间<b>相等</b>时，返回true
	 * @param first 待比较的第一个Date对象
	 * @param second 待比较的第二个Date对象
	 * @return
	 */
	public static final boolean equal( java.util.Date first , java.util.Date second ){
		if( first !=null ){
			return first.equals( second );
		}
		return false ;
	}

	/**
	 * 当第一个参数first所表示的时间比第二个参数second所表示的时间<b>晚</b>时，返回true
	 * @param first 待比较的第一个Date对象
	 * @param second 待比较的第二个Date对象
	 * @return
	 */
	public static final boolean after( java.util.Date first , java.util.Date second ){
		if( first !=null && second != null ){
			return first.before( second );
		}
		return false ;
	}

    /**
     * 返回一批 java.util.Date 对象中，所表示的时间最早的那个 Date 对象
     * @param first 参与比较的第一个 Date 对象
     * @param more 参与比较的其它 Date 对象
     * @return
     */
	public static final java.util.Date earliest( java.util.Date first , java.util.Date ... more ) {
	    if( first == null || more == null ){
            throw new RuntimeException( "被比较的日期对象不能为NULL" );
        }

	    if( more.length > 0 ){

            // 获得第一个 Date 对象所表示时间的毫秒值
            long min = first.getTime() ;
            int index = -1 ; // 声明用于记录最小值在数组中索引的变量
            // 通过循环找出最小值( 最小值即为时间最早 ) [ 为降低难度才使用 for 循环 ]
            for( int i = 0 ; i < more.length ; i++ ){
                java.util.Date date = more[ i ] ;
                if( date == null ){
                    throw new RuntimeException( "被比较的日期对象不能为NULL" );
                }
                long time = date.getTime() ;
                if(  time < min ){
                    min = time;
                    index = i ;
                }
            }

            return index == -1 ? first : more[ index ] ;

        } else {
	        return first ;
        }

    }

    /**
     * 返回一批 java.util.Date 对象中，所表示的时间最晚的那个 Date 对象
     * @param first 参与比较的第一个 Date 对象
     * @param more 参与比较的其它 Date 对象
     * @return
     */
    public static final java.util.Date latest( java.util.Date first , java.util.Date ... more ) {
        if( first == null || more == null ){
            throw new RuntimeException( "被比较的日期对象不能为NULL" );
        }

        if( more.length > 0 ){

            // 获得第一个 Date 对象所表示时间的毫秒值
            long max = first.getTime() ;
            int index = -1 ; // 声明用于记录最小值在数组中索引的变量
            // 通过循环找出最小值( 最小值即为时间最早 ) [ 为降低难度才使用 for 循环 ]
            for( int i = 0 ; i < more.length ; i++ ){
                java.util.Date date = more[ i ] ;
                if( date == null ){
                    throw new RuntimeException( "被比较的日期对象不能为NULL" );
                }
                long time = date.getTime() ;
                if(  time > max ){
                    max = time;
                    index = i ;
                }
            }

            return index == -1 ? first : more[ index ] ;

        } else {
            return first ;
        }

    }

	/***
	 * 返回以毫秒数表示的两个日期值的时间差，计算规则为 first - second
	 * @param first
	 * @param second
	 * @return
	 */
	public static final long difference( java.util.Date first , java.util.Date second ){
		if( first == null || second == null ){
			throw new RuntimeException( "被比较的日期对象不能为NULL" );
		}
		return first.getTime() - second.getTime() ;
	}

	/**
	 * 返回两个 java.util.Date 对象相差的月数 ( 计算规则为 first - second )
	 * @param first 用于获取时间差的第一个 java.util.Date 对象
	 * @param second 用于获取时间差的第二个 java.util.Date 对象
	 * @return 返回两个java.util.Date对象所表示时间相差的月数。如果 第一个参数 first 表示的时间晚于 第二个参数 second 表示的时间，返回负数。
	 */
	public static final int ofMonth( java.util.Date first , java.util.Date second ) {
        //  计算时间差
        long duration = difference( first , second );
        // 统计月数
        long months = duration / ( DAY  * 31 ) ; //  已知问题:  如果年份相差的足够久远，统计的月数误差可能越大
        return (int)months ;
	}

    /**
     * 返回两个 java.util.Date 对象相差的天数 ( 计算规则为 first - second )
     * @param first 用于获取时间差的第一个 java.util.Date 对象
     * @param second 用于获取时间差的第二个 java.util.Date 对象
     * @return 返回两个java.util.Date对象所表示时间相差的天数。如果 第一个参数 first 表示的时间晚于 第二个参数 second 表示的时间，返回负数。
     */
    public static final int ofDay( java.util.Date first , java.util.Date second ) {
        //  计算时间差
        long duration = difference( first , second );
        // 统计天数
        long days = duration / DAY ;
	    return (int)days ;
    }
	
}
